JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
利用 flexbox、transition 等 CSS 屬性,搭配 JS 監聽 transitionend、click 事件,最終實作出一個美觀的 Image Gallery。
由最外層的.panels包覆住內部的5個.panel所形成的一個巢狀結構。
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
(僅說明影片中更動的部分)
先將最外層的 .panels 顯示類型設定為 flex,它同時也作為一個 flex-container 包覆住內部的五個 flex-items 也就是 .panel。
.panels {
/*其餘略過*/
display: flex;
}
flex 是 flex-grow、flex-shrink、flex-basis 的簡寫,只有指定一個值給 flex 時,則代表設定的是 flex-grow,其餘屬性以預設值帶入。
flex-grow 可以指定 flex-container 的剩餘空間該如何分配,下面所有的 .panel 的 flex-grow 都是 1,也就是均勻分配剩餘空間。
接著,將每一個 .panel 的顯示類型都設定為 flex (此時的.panel 對下面的<p></p>來說就是 flex-container) 並將其下的 flex-item 在水平、鉛直方向都置中。
最後,設定 flex-direction 將 flex-box 的 main-axis 更改為直列。
.panel {
flex: 1; /*將每個 flex-item 的大小都設為一樣並填滿*/
display: flex;
justify-content: center; /*在水平方向置中*/
align-items: center;/*在鉛直方向置中*/
flex-direction: column;
}
將 .panel (flex-container)下的 <p></p> (flex-item) 的 flex 屬性設定為 1、0、auto,也就是均勻分配 flex-container 的剩餘空間、flex-item 長度超過 flex-container 時的收縮量設為 0、flex-item 在 flex container 的初始大小設為自動(auto)。
接著,也將<p></p>當作是一個 flex-container,設定顯示類型為 flex。透過 justify-content、align-items,將標籤內的文字(flex-item) 水平、鉛直置中排列。
.panel > * {
/*其餘省略*/
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
初始狀態,分別將在 .panel 上方和下方的 <p></p> 都各上移、下移 100% 以達到隱藏的效果。
.panel > *:first-child{
transform: translateY(-100%);/*上移*/
}
.panel > *:last-child{
transform: translateY(100%);/*下移*/
}
接著設定當.panel上有open-active這個 class 時,就將原本隱藏的文字分別下移和上移顯示出來。
.panel.open-active > *:first-child{
transform: translateY(0);
}
.panel.open-active > *:last-child{
transform: translateY(0);
}
.panel開啟時,將內部文字放大為 40px 並將 flex-container 剩餘分配的位置變為原來的5倍。
.panel.open {
font-size: 40px;
flex: 5;
}
CSS flex 屬性
圖解 Flexbox 基本屬性
FLEXBOX FROGGY-學習 flexbox 的小遊戲
取得所有的.panel 並放到 NodeList 'panels' 中。
const panels = document.querySelectorAll('.panel');
為 panels 中的每一個.panel都註冊兩個事件監聽器,當 click 或 transitionend 事件發生時,就分別以 toggleOpen 和 toggleActive 方法進行事件處理。
panels.forEach(panel => panel.addEventListener('click',toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend',toggleActive));
點擊任意一個.panel,toggleOpen() 方法會替觸發事件的.panel依照情況的不同,新增或是移除.open 這個 class,若是觸發事件的.panel原本沒有.open則新增,有的話則移除.open。
當 transitionend 事件發生,toggleActive() 會作出如同 toggleOpen() 一樣的判斷,決定新增還是移除.open-active這個 class。因為同時會被觸發的 transitionend 事件有很多個,我們決定在觸發 transitionend 事件的 CSS 屬性是 flex 時,才採取處理。為什麼不寫 e.propertyName == 'flex-grow' 是因為在 Safari 顯示的是 flex 而 Chrome、FireFox 顯示的是 flex-grow,為避免這個差異導致錯誤,我們可以使用 include('flex'),當 propertyName 含有 flex 就進行事件處理。
function toggleOpen(){
this.classList.toggle('open');
}
function toggleActive(e){
if(e.propertyName.includes('flex')){
this.classList.toggle('open-active');
}
}
Element.classList
String.prototype.includes()